"""Provide some widely useful utilities. Safe for "from utils import *".

"""

import random

def mean(values):
    """Return the arithmetic average of the values."""
    return sum(values) / float(len(values))


def count_if(predicate, seq):
    """Count the number of elements of seq for which the predicate is true.
    >>> count_if(callable, [42, None, max, min])
    2
    """
    f = lambda count, x: count + (not not predicate(x))
    return reduce(f, seq, 0)


def normalize(numbers):
    """Multiply each number by a constant such that the sum is 1.0
    >>> normalize([1,2,1])
    [0.25, 0.5, 0.25]
    """
    total = float(sum(numbers))
    return [n / total for n in numbers]


def probability(p):
    "Return true with probability p."
    assert 0.0 <= p <= 1.0
    return p > random.uniform(0.0, 1.0)


def _isnumber(x):
    return hasattr(x, '__int__')


def num_or_str(x):
    if _isnumber(x): return x
    try:
        return int(x) 
    except ValueError:
        try:
            return float(x) 
        except ValueError:
                return str(x).strip() 


def unique(seq):
    return list(set(seq))


def removeall(item, seq):
    if isinstance(seq, str):
        return seq.replace(item, '')
    return [x for x in seq if x != item]


def _AIMAFile(components, mode='r'):
    "Open a file based at the AIMA root directory."
    import os.path as os_path
    import utils
    folder = os_path.dirname(utils.__file__)
    return open(apply(os_path.join, [folder] + components), mode)


def DataFile(name, mode='r'):
    "Return a file in the AIMA /data directory."
    return _AIMAFile(['..', 'data', name], mode)

def argmax_random_tie(seq, fn):
    """Return an element with lowest fn(seq[i]) score; break ties at random.
    Thus, for all s,f: argmax_random_tie(s, f) in argmin_list(s, f)"""
    best_score = fn(seq[0])
    n = 0
    for x in seq:
        x_score = fn(x)
        if x_score > best_score:
            best, best_score = x, x_score
            n = 1
        elif x_score == best_score:
            n += 1
            if random.randrange(n) == 0:
                best = x
    return best
